home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Miscellaneous / Headlines Code / Headlines Project ƒ / SpyooDisplay.c < prev    next >
Text File  |  1992-11-23  |  17KB  |  708 lines

  1. /*
  2.  * SpyooDisplay.c
  3.  * Version 1.0.1
  4.  *
  5.  * This is © Copyright 1992 by Jamie R. McCarthy.  All rights reserved.
  6.  * Please see HeadlinesMain.c for distribution information.
  7.  *
  8.  * All the code related to displaying a headline on the screen is in
  9.  * this file.
  10.  *
  11.  * Again, I apologize for the non-prettified code.  Things could
  12.  * certainly be made much nicer by splitting up some of the long
  13.  * functions, and maybe making two or three files out of this one.
  14.  * Maybe next release...
  15.  *
  16.  */
  17.  
  18.  
  19. /******************************/
  20.  
  21. #include "SpyooDisplay.h"
  22.  
  23. /******************************/
  24.  
  25. #include <Global.h>
  26. #include <ctype.h>
  27. #include <string.h>
  28. #include <stdlib.h>
  29.  
  30. #include <QuickDraw.h>
  31. #include <NeoTextBox.h>
  32. #include <ATMInterface.h>
  33.  
  34. #include "JMassMalloc.h"
  35. #include "Spyoo.h"
  36.  
  37. /******************************/
  38.  
  39. #define kMinSnugFactor (8)
  40.  
  41. /******************************/
  42.  
  43. class gFontClass;
  44. Boolean usingATM;
  45. CTabHandle possibleColors;
  46.  
  47. void getMonitorInfo(GMParamBlockPtr params,
  48.     Rect *monitorBounds, Boolean *monitorSupportsColor);
  49. void getWrapBox(GMParamBlockPtr params, Rect *monitorBounds, Rect *theWrapBox);
  50. void setTextJustification(GMParamBlockPtr params,
  51.     Rect *theMonitorBounds, Rect *theWrapBox,
  52.     short *theTextJustification);
  53. void setTextFont(GMParamBlockPtr params, defn *theDefn, short *theFontNum);
  54. void setTextStyle(GMParamBlockPtr params, defn *theDefn, Rect *theWrapBox,
  55.     short fontNum, short *theFontStyle);
  56. Boolean setTextSize(GMParamBlockPtr params, defn *theDefn,
  57.     Rect *theMonitorBounds, Rect *theWrapBox,
  58.     short fontNum, short fontStyle, Boolean scaleBitmapFonts, short *theFontSize);
  59. void setTextColor(GMParamBlockPtr params, Boolean monitorSupportsColor);
  60. void resetForeColor(GMParamBlockPtr params);
  61. short getMinWidth(short *descent);
  62.  
  63. /******************************/
  64.  
  65.  
  66.  
  67. OSErr setupDisplay(void)
  68. {
  69.         /*
  70.          * Change the "file" being read (actually a resource) to the
  71.          * resource that describes the fonts and styles, then read
  72.          * that information into a bastardized "class," changing the
  73.          * C strings with the name and "tags" into Pascal strings
  74.          * with the possible styles, terminated by a right brace,
  75.          * following the last string character.
  76.          *
  77.          * This is known as a "skanky hack."  Close your eyes.
  78.          * Things might get ugly.
  79.          */
  80.     
  81.     OSErr theOSErr;
  82.     char **saveGInRsrcHndl = gInRsrcHndl;
  83.     long saveGInRsrcMark = gInRsrcMark;
  84.     long saveGInRsrcLength = gInRsrcLength;
  85.     short oldState = HGetState(gInRsrcHndl);
  86.     
  87.     theOSErr = noErr;
  88.     
  89.     HLock(gInRsrcHndl);
  90.     
  91.     possibleColors = NULL;
  92.     gInRsrcHndl = NULL;
  93.     
  94.     usingATM = (initVersionATM(1) != 0); // let's try version 1
  95.     
  96.     SetResLoad(FALSE);
  97.     possibleColors = (CTabHandle) GetResource('clut', 999);
  98.     SetResLoad(TRUE);
  99.     if (possibleColors != NULL) {
  100.         LoadResource(possibleColors);
  101.         if (*possibleColors == NULL) {
  102.             theOSErr = memFullErr;
  103.         } else {
  104.             DetachResource(possibleColors);
  105.         }
  106.     }
  107.     
  108.     if (theOSErr == noErr) {
  109.         SetResLoad(FALSE);
  110.         gInRsrcHndl = GetResource('TEXT', 129);
  111.         SetResLoad(TRUE);
  112.         if (gInRsrcHndl == NULL) {
  113.             theOSErr = noFontInfoErr;
  114.         } else {
  115.             LoadResource(gInRsrcHndl);
  116.             if (*gInRsrcHndl == NULL) {
  117.                 theOSErr = memFullErr;
  118.             } else {
  119.                 gInRsrcLength = GetHandleSize(gInRsrcHndl);
  120.                 gInRsrcMark = 0;
  121.             }
  122.         }
  123.     }
  124.     
  125.     if (theOSErr == noErr) {
  126.         
  127.         register defn **update;
  128.         defn *dp;
  129.         short nFontsFound = 0;
  130.         
  131.         readLine();
  132.         
  133.         update = &(gFontClass.list);
  134.         
  135.         do {
  136.             OSErr theOSErr;
  137.             theOSErr = process(&dp);
  138.             if (theOSErr == noErr) {
  139.                 
  140.                 short theFontNum;
  141.                 Boolean useThisFont;
  142.                 char *leftBrace;
  143.                 
  144.                 useThisFont = TRUE;
  145.                 CtoPstr(dp->str);
  146.                 if (dp->str[0] > 0) {
  147.                     leftBrace = strchr(&dp->str[1], LBRACE);
  148.                     dp->str[0] = (leftBrace - &dp->str[0]) - 1;
  149.                 }
  150.                 GetFNum(dp->str, &theFontNum);
  151.                 if (theFontNum != 0) {
  152.                     *update = dp;
  153.                     gFontClass.weight += dp->cumul;
  154.                     dp->cumul = gFontClass.weight;
  155.                     update = &(dp->next);
  156.                     ++nFontsFound;
  157.                 } else {
  158.                         /*
  159.                          * Since I'm using JMassMalloc, free() doesn't actually
  160.                          * _do_ anything;  however, if it did, I'd write:
  161.                     free(dp);
  162.                         */
  163.                 }
  164.                 
  165.             }
  166.         } while (theOSErr == noErr
  167.             && nFontsFound < kMaxNFonts
  168.             && (readLine(), gInLine[0] != '%'));
  169.         
  170.     }
  171.     
  172.     if (gInRsrcHndl != NULL) {
  173.         ReleaseResource(gInRsrcHndl);
  174.     }
  175.     
  176.     if (theOSErr == noFontInfoErr) {
  177.         gFontClass.weight = 1;
  178.         gFontClass.list = malloc( sizeof(defn) );
  179.         if (gFontClass.list == NULL) {
  180.             theOSErr = memFullErr;
  181.         } else {
  182.             gFontClass.list->cumul = 1;
  183.             gFontClass.list->str = (char*) "\pGeneva{18bbib}";
  184.             gFontClass.list->next = NULL;
  185.         }
  186.     }
  187.     
  188.     gInRsrcHndl = saveGInRsrcHndl;
  189.     gInRsrcMark = saveGInRsrcMark;
  190.     gInRsrcLength = saveGInRsrcLength;
  191.     
  192.     HSetState(gInRsrcHndl, oldState);
  193.     
  194.     return theOSErr;
  195. }
  196.  
  197.  
  198.  
  199. OSErr display(GMParamBlockPtr params, RgnHandle blankRgn,
  200.     char *s, short deftag, unsigned short maxNTicksToSnuggle,
  201.     Boolean mustDrawNow)
  202.     /*
  203.      * Given the name of a class, terminated by SLASH, display() will
  204.      * (1) expand the class descriptor with evaluate();  (2) pick a font,
  205.      * font size, font style, and location;  (3) write the evaluated
  206.      * descriptor to the screen.
  207.      */
  208. {
  209.     OSErr theOSErr;
  210.     long initialTicks;
  211.     
  212.     theOSErr = noErr;
  213.     initialTicks = TickCount();
  214.     gHeadline[0] = 0;
  215.     theOSErr = evaluate(s, deftag);
  216.     
  217.     if (theOSErr == noErr) {
  218.         
  219.         Boolean doneOK, giveUp;
  220.         
  221.         doneOK = FALSE;
  222.         giveUp = FALSE;
  223.         
  224.         while (!doneOK && (!giveUp || mustDrawNow)) {
  225.             Rect theMonitorBounds;
  226.             Boolean monitorSupportsColor;
  227.             Rect theWrapBox;
  228.             unsigned short snugFactor;
  229.             short nLinesTotal, initialNLinesTotal;
  230.             defn theDefn;
  231.             short minWidth, descent;
  232.             short theTextJustification;
  233.             short theFontNum, theFontSize, theFontStyle;
  234.             
  235.             getMonitorInfo(params, &theMonitorBounds, &monitorSupportsColor);
  236.             getWrapBox(params, &theMonitorBounds, &theWrapBox);
  237.             theTextJustification = teFlushLeft; // during testing
  238.             setTextFont(params, &theDefn,
  239.                 &theFontNum);
  240.             setTextStyle(params, &theDefn, &theWrapBox, theFontNum,
  241.                 &theFontStyle);
  242.             doneOK = setTextSize(params, &theDefn,
  243.                 &theMonitorBounds, &theWrapBox,
  244.                 theFontNum, theFontStyle, mustDrawNow, &theFontSize);
  245.             
  246.             if (doneOK || mustDrawNow) {
  247.                 minWidth = getMinWidth(&descent);
  248.                 if (theWrapBox.right - theWrapBox.left < minWidth) {
  249.                     theWrapBox.right = theWrapBox.left + minWidth;
  250.                     if (theWrapBox.right > theMonitorBounds.right) {
  251.                         theWrapBox.left -= theWrapBox.right - theMonitorBounds.right;
  252.                         theWrapBox.right = theMonitorBounds.right;
  253.                         if (theWrapBox.left < theMonitorBounds.left) {
  254.                                 /* Too big a font and too much text.  Give up and try
  255.                                  * again with different text parameters. 
  256.                                  */
  257.                             doneOK = FALSE;
  258.                         }
  259.                     }
  260.                 }
  261.             }
  262.             
  263.             if (doneOK || mustDrawNow) {
  264.                 
  265.                 short endY;
  266.                 
  267.                     /* Figure out how many lines there are. */
  268.                 HidePen();
  269.                 nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox, 
  270.                     theTextJustification,
  271.                     0,                                    // line height code
  272.                     &endY, NULL);
  273.                 endY += descent;
  274.                 
  275.                     /* Fit the wrapbox snugly around the text. */
  276.                 if (endY < theWrapBox.bottom) {
  277.                     theWrapBox.top += (theWrapBox.bottom - endY) / 2;
  278.                 } else {
  279.                     theWrapBox.top -= (endY - theWrapBox.bottom);
  280.                     if (theWrapBox.top < theMonitorBounds.top) {
  281.                         doneOK = FALSE;
  282.                     }
  283.                 }
  284.                 
  285.                 if (doneOK) {
  286.                         /* See if the wrapbox can be made snugger without adding extra lines. */
  287.                     initialNLinesTotal = nLinesTotal;
  288.                     snugFactor = (theWrapBox.right - theWrapBox.left) / 3;
  289.                     while (nLinesTotal == initialNLinesTotal
  290.                         && initialTicks + maxNTicksToSnuggle > TickCount()
  291.                         && (theWrapBox.right - theWrapBox.left) > minWidth) {
  292.                         
  293.                         if ((theWrapBox.right - theWrapBox.left) - snugFactor < minWidth) {
  294.                             snugFactor = (theWrapBox.right - theWrapBox.left) - minWidth;
  295.                         }
  296.                         
  297.                         theWrapBox.left += snugFactor / 2;  theWrapBox.right -= snugFactor / 2;
  298.                         nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox, 
  299.                             theTextJustification,
  300.                             0,                            // line height code
  301.                             NULL, NULL);
  302.                         if (nLinesTotal != initialNLinesTotal) {
  303.                                 /* Whoops, we went too far. */
  304.                             theWrapBox.left -= snugFactor / 2;  theWrapBox.right += snugFactor / 2;
  305.                             snugFactor = (snugFactor * 3) / 8;
  306.                             if (snugFactor < kMinSnugFactor) {
  307.                                     /* OK, it's tight enough.  Quit trying. */
  308.                             } else {
  309.                                     /* Let's keep trying to make it snugger. */
  310.                                 nLinesTotal = initialNLinesTotal;
  311.                             }
  312.                         }
  313.                         
  314.                     }
  315.                 }
  316.                 
  317.                 ShowPen();
  318.                 if (doneOK || mustDrawNow) {
  319.                         /* Do it! */
  320.                     setTextColor(params, monitorSupportsColor);
  321.                     setTextJustification(params,
  322.                         &theMonitorBounds, &theWrapBox,
  323.                         &theTextJustification);
  324.                     nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox, 
  325.                         theTextJustification,
  326.                         0,                            // line height code
  327.                         NULL, NULL);
  328.                     resetForeColor(params);
  329.                     if (mustDrawNow) {
  330.                         doneOK = TRUE;
  331.                     }
  332.                 }
  333.             }
  334.             
  335.             if (!doneOK
  336.                 && !mustDrawNow
  337.                 && TickCount() > initialTicks + maxNTicksToSnuggle) {
  338.                 giveUp = TRUE; 
  339.             }
  340.         }
  341.         
  342.         if (giveUp && !mustDrawNow) {
  343.             theOSErr = retryDisplayErr;
  344.         }
  345.         
  346.     }
  347.     
  348.     return theOSErr;
  349. }
  350.  
  351.  
  352.  
  353. OSErr remove(GMParamBlockPtr params, RgnHandle blankRgn)
  354.     /*
  355.      * remove() "undraws" the current headline.
  356.      */
  357. {
  358.     PenPat(params->qdGlobalsCopy->qdBlack);
  359.     PenMode(patCopy);
  360.     PaintRgn(blankRgn);
  361.     return noErr;
  362. }
  363.  
  364.  
  365.  
  366. OSErr shutdownDisplay(GMParamBlockPtr params)
  367. {
  368.     if (possibleColors != NULL) {
  369.         DisposHandle(possibleColors);
  370.         possibleColors = NULL;
  371.     }
  372. }
  373.  
  374.  
  375.  
  376. /******************************/
  377.  
  378.  
  379.  
  380. void getMonitorInfo(GMParamBlockPtr params,
  381.     Rect *monitorBounds, Boolean *monitorSupportsColor)
  382. {
  383.     short wMonitor;
  384.     wMonitor = jrLinearShort(&gJR, 0, params->monitors->monitorCount - 1);
  385.     *monitorBounds = params->monitors->monitorList[wMonitor].bounds;
  386.     *monitorSupportsColor = (params->monitors->monitorList[wMonitor].curDepth > 2);
  387. }
  388.  
  389.  
  390.  
  391. void getWrapBox(GMParamBlockPtr params, Rect *monitorBounds, Rect *theWrapBox)
  392. {
  393.     unsigned short monitorWidth, monitorHeight;
  394.     short width, height;
  395.     short left, top;
  396.     
  397.     monitorWidth = monitorBounds->right - monitorBounds->left;
  398.     monitorHeight = monitorBounds->bottom - monitorBounds->top;
  399.     
  400.     width = jrLinearShort(&gJR, monitorWidth/2, (monitorWidth*3)/4);
  401.     height = jrLinearShort(&gJR, monitorHeight/3, (monitorHeight*3)/4);
  402.     left = jrLinearShort(&gJR, monitorBounds->left, monitorBounds->right - width);
  403.     top = jrLinearShort(&gJR, monitorBounds->top, monitorBounds->bottom - height);
  404.     
  405.     SetRect(theWrapBox, left, top, left+width, top+height);
  406. }
  407.  
  408.  
  409.  
  410. void setTextJustification(GMParamBlockPtr params,
  411.     Rect *theMonitorBounds, Rect *theWrapBox,
  412.     short *theTextJustification)
  413. {
  414.     short leftMargin, rightMargin;
  415.     short theJust;
  416.     leftMargin = (theWrapBox->left - theMonitorBounds->left);
  417.     rightMargin = (theMonitorBounds->right - theWrapBox->right);
  418.     
  419.     if (leftMargin == 0) {
  420.         *theTextJustification = teFlushLeft;
  421.     } else if (rightMargin == 0) {
  422.         *theTextJustification = teFlushRight;
  423.     } else {
  424.         
  425.         theJust = (leftMargin > rightMargin) ?
  426.             (leftMargin / rightMargin)
  427.             : -(rightMargin / leftMargin);
  428.         
  429.         theJust += jrLinearShort(&gJR, -2, 2);
  430.         
  431.         if (theJust < -1) {
  432.             *theTextJustification = teFlushLeft;
  433.         } else if (theJust > 1) {
  434.             *theTextJustification = teFlushRight;
  435.         } else {
  436.             *theTextJustification = teCenter;
  437.         }
  438.         
  439.     }
  440. }
  441.  
  442.  
  443.  
  444. void setTextFont(GMParamBlockPtr params, defn *theDefn, short *theFontNum)
  445. {
  446.     defn *dp;
  447.     short fontNum;
  448.     Boolean fontIsOK;
  449.     
  450.     fontIsOK = FALSE;
  451.     while (!fontIsOK) {
  452.         register short i;
  453.         short wFont;
  454.         i = jrLinearShort(&gJR, 0, gFontClass.weight-1);
  455.         dp = gFontClass.list;
  456.         wFont = 0;
  457.         while (dp->cumul <= i) {
  458.             dp = dp->next;
  459.             ++wFont;
  460.         }
  461.         fontIsOK = ( wFont <= kMaxNFonts );
  462.         if (!fontIsOK) DebugStr("\pfontNotOK!?!");
  463.     }
  464.     
  465.     GetFNum(dp->str, &fontNum);
  466.     if (fontNum == 0) DebugStr("\pbad font name");
  467.     TextFont( fontNum );
  468.     
  469.     *theDefn = *dp;
  470.     *theFontNum = fontNum;
  471. }
  472.  
  473.  
  474.  
  475. void setTextStyle(GMParamBlockPtr params, defn *theDefn, Rect *theWrapBox,
  476.     short theFontNum, short *theFontStyle)
  477. {
  478.     unsigned char *cp;
  479.     short theStyle;
  480.     Boolean mustBePlain;
  481.     short chance;
  482.     
  483.     cp = (unsigned char *) &theDefn->str[ theDefn->str[0] + 1];
  484.     theStyle = normal;
  485.     chance = 1;
  486.     mustBePlain = FALSE;
  487.     
  488.     while (*cp != RBRACE && !mustBePlain) {
  489.         if (jrLinearShort(&gJR, 0, chance) == 0) {
  490.             switch (*cp) {
  491.                 case 'p':    theStyle = normal;        mustBePlain = TRUE;    break;
  492.                 case 'b':    theStyle |= bold;                                        break;
  493.                 case 'i':    theStyle |= italic;                                    break;
  494.                 case 'o':    theStyle |= outline;                                    break;
  495.                 case 's':    theStyle |= shadow;                                    break;
  496.                 case 'c':    theStyle |= condense;                                break;
  497.                 case 'e':    theStyle |= extend;                                    break;
  498.             }
  499.         }
  500.         ++cp;
  501.         chance <<= 1;
  502.     }
  503.     
  504.     if (mustBePlain) {
  505.         TextFace(normal);
  506.     } else {
  507.         TextFace(theStyle);
  508.     }
  509.     
  510.     *theFontStyle = theStyle;
  511. }
  512.  
  513.  
  514.  
  515. Boolean setTextSize(GMParamBlockPtr params, defn *theDefn,
  516.     Rect *theMonitorBounds, Rect *theWrapBox,
  517.     short theFontNum, short theFontStyle,
  518.     Boolean scaleBitmapFonts, short *theFontSize)
  519. {
  520.     short size;
  521.     short width, height;
  522.     short charsPerLineToTryFor;
  523.     short nLinesToTryFor;
  524.     unsigned char *cp;
  525.     short minSize;
  526.     short i;
  527.     Boolean foundOK;
  528.     
  529.     foundOK = TRUE;
  530.     
  531.     cp = (unsigned char *) &theDefn->str[ theDefn->str[0] + 1 ] + 1;
  532.     minSize = 0;
  533.     while ( isdigit(*cp) ) {
  534.         minSize *= 10;
  535.         minSize += (*(cp++) - '0');
  536.     }
  537.     
  538.         /*
  539.          * All "magic numbers" in the following section are completely arbitrary.
  540.          * I messed around until I got something that works OK.
  541.          */
  542.     width = theWrapBox->right - theWrapBox->left;
  543.     width = Min(width, 400);
  544.     height = theWrapBox->bottom - theWrapBox->top;
  545.     height = Min(height, 300);
  546.     charsPerLineToTryFor = Max(5, Min(30, width/15));
  547.     nLinesToTryFor = Max(2, Min(6, height/40));
  548.     while (gHeadline[0] < (charsPerLineToTryFor*nLinesToTryFor)/2) {
  549.         if (charsPerLineToTryFor == 5 && nLinesToTryFor == 2) break;
  550.         charsPerLineToTryFor = Max(5, charsPerLineToTryFor-2);
  551.         nLinesToTryFor = Max(2, nLinesToTryFor-1);
  552.     }
  553.     while (gHeadline[0] > (charsPerLineToTryFor*nLinesToTryFor)*2) {
  554.         if (charsPerLineToTryFor == 30 && nLinesToTryFor == 6) break;
  555.         charsPerLineToTryFor = Min(30, charsPerLineToTryFor+2);
  556.         nLinesToTryFor = Min(6, nLinesToTryFor+1);
  557.     }
  558.     size = Min( (width*2)/charsPerLineToTryFor, height/nLinesToTryFor );
  559.     
  560.         /*
  561.          * Fiddle with it a bit.
  562.          */
  563.     size += jrLinearShort(&gJR, -3, 3);
  564.     size = (size > 48) ?
  565.         ( (size+6) / 12 ) * 12
  566.         : ( (size+3) / 6 ) * 6;
  567.     size = Max(minSize, Min(72, size));
  568.     
  569.     if (!scaleBitmapFonts) {
  570.         
  571.         if (!fontAvailableATM(theFontNum, theFontStyle)) {
  572.             
  573.             foundOK = (RealFont(theFontNum, size));
  574.             
  575.                 /*
  576.                  * Can we get a real font if we go a few sizes bigger?
  577.                  */
  578.             if (!foundOK) {
  579.                 for (i = 1; i <= 4; ++i) {
  580.                     if (size+i <= 72 && RealFont(theFontNum, size+i)) {
  581.                         size += i;
  582.                         foundOK = TRUE;
  583.                         break;
  584.                     }
  585.                 }
  586.             }
  587.                 /*
  588.                  * How about a few sizes smaller?
  589.                  */
  590.             if (!foundOK) {
  591.                 for (i = 1; i <= 10; ++i) {
  592.                     if (size-i >= minSize && RealFont(theFontNum, size-i)) {
  593.                         size -= i;
  594.                         foundOK = TRUE;
  595.                         break;
  596.                     }
  597.                 }
  598.             }
  599.             
  600.         }
  601.         
  602.     }
  603.     
  604.     TextSize( size );
  605.     *theFontSize = size;
  606.     
  607.     return foundOK;
  608. }
  609.  
  610.  
  611.  
  612. void setTextColor(GMParamBlockPtr params, Boolean monitorSupportsColor)
  613. {
  614.     if (params->colorQDAvail
  615.         && monitorSupportsColor
  616.         && params->controlValues[kColorControl]
  617.         && possibleColors != NULL) {
  618.         
  619.         short wColor;
  620.         
  621.         wColor = jrLinearShort(&gJR, 0, (**possibleColors).ctSize);
  622.         HLock( (Handle) possibleColors );
  623.         RGBForeColor( & (**possibleColors).ctTable[wColor].rgb );
  624.         HUnlock( (Handle) possibleColors );
  625.         TextMode(srcOr);
  626.         
  627.     } else {
  628.         TextMode(srcBic);
  629.     }
  630. }
  631.  
  632.  
  633.  
  634. void resetForeColor(GMParamBlockPtr params)
  635. {
  636.     if (params->colorQDAvail) {
  637.         RGBColor myBlack;
  638.         myBlack.red = myBlack.green = myBlack.blue = 0;
  639.         RGBForeColor(&myBlack);
  640.     }
  641. }
  642.  
  643.  
  644.  
  645.     static short getWordWidth(unsigned char *wordPtr, short nChars);
  646.     static short getWordWidth(unsigned char *wordPtr, short nChars)
  647.     {
  648.         PenState oldPS;
  649.         Point penLoc;
  650.         GetPenState(&oldPS);
  651.         HidePen();
  652.         MoveTo(0, 0);
  653.         DrawText(wordPtr, 0, nChars);
  654.         GetPen(&penLoc);
  655.         ShowPen();
  656.         SetPenState(&oldPS);
  657.         return penLoc.h;
  658.     }
  659.     
  660.     static short getWidestWordWidth(short widMax);
  661.     static short getWidestWordWidth(short widMax)
  662.     {
  663.         short cChar;
  664.         short nChars = gHeadline[0];
  665.         short cLength = 0, cWidth, widestWidth = 0;
  666.         unsigned char *cp = &gHeadline[1];
  667.         unsigned char *wordPtr;
  668.         
  669.         wordPtr = NULL;
  670.         for (cChar = 0; cChar < nChars; ++cChar) {
  671.             if ( isspace(*cp) ) {
  672.                 if (cLength*widMax >= widestWidth) {
  673.                     wordPtr = cp - cLength;
  674.                     cWidth = getWordWidth(wordPtr, cLength);
  675.                     if (cWidth > widestWidth) {
  676.                         widestWidth = cWidth;
  677.                     }
  678.                 }
  679.                 cLength = 0;
  680.             } else {
  681.                 ++cLength;
  682.             }
  683.             ++cp;
  684.         }
  685.         
  686.         if (cLength*widMax >= widestWidth) {
  687.             wordPtr = cp - cLength;
  688.             cWidth = getWordWidth(wordPtr, cLength);
  689.             if (cWidth > widestWidth) {
  690.                 widestWidth = cWidth;
  691.             }
  692.         }
  693.         
  694.         return widestWidth;
  695.     }
  696.     
  697. short getMinWidth(short *descent)
  698. {
  699.     FontInfo theFI;
  700.     unsigned char *theWord;
  701.     
  702.     GetFontInfo(&theFI);
  703.     if (descent != NULL) *descent = theFI.descent;
  704.     
  705.     return getWidestWordWidth(theFI.widMax) + 1; // 1 is an arbitrary slop factor
  706. }
  707.  
  708.